package wx

import (
	"bytes"
	"encoding/json"
	"errors"
	"github.com/go-emix/emix-logrus"
	"io"
	"net/http"
	"service/common/aliyun/oss"
	"time"
)

const (
	Code2SessionApi = "https://api.weixin.qq.com/sns/jscode2session" +
		"?appid=" + AppId + "&secret=" + Secret + "&grant_type=authorization_code&js_code="
	GetAccessTokenApi = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential" +
		"&appid=" + AppId + "&secret=" + Secret
	GetUnlimitedApi = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token="
	AppId           = ""
	Secret          = ""
	// 商家id
	Mchid = ""
	// 支付秘钥
	WxPayKey = ""
	// 订阅消息
	MessageSubscribeSendApi = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token="
	// 外部跳转URL Link
	UrlLinkApi = "https://api.weixin.qq.com/wxa/generate_urllink?access_token="
	// 微信token无效错误码
	AccessTokenInvalidErrCode = 40001
)

type OpenidResp struct {
	Openid     string
	SessionKey string `json:"session_key"`
	Unionid    string
	Errcode    int
	Errmsg     string
}

func GetOpenid(jsCode string) (string, error) {
	get, err := http.Get(Code2SessionApi + jsCode)
	if err != nil {
		return "", err
	}
	defer func() {
		_ = get.Body.Close()
	}()
	all, err := io.ReadAll(get.Body)
	if err != nil {
		return "", err
	}
	var or OpenidResp
	err = json.Unmarshal(all, &or)
	if err != nil {
		return "", err
	}
	if or.Errcode != 0 {
		return "", errors.New(or.Errmsg)
	}
	return or.Openid, nil
}

type AccessTokenResp struct {
	AccessToken string `json:"access_token"`
	ExpiresIn   int    `json:"expires_in"`
	Errcode     int
	Errmsg      string
}

var AccessToken = ""

func init() {
	refreshAccessToken()
	refreshJsTicket()
}

// 刷新AccessToken
func refreshAccessToken() {
	get, err := http.Get(GetAccessTokenApi)
	if err != nil {
		emlogrus.Error("刷新accessToken错误：", err)
		return
	}
	defer func() {
		_ = get.Body.Close()
	}()
	all, err := io.ReadAll(get.Body)
	if err != nil {
		emlogrus.Error("刷新accessToken错误：", err)
		return
	}
	var at AccessTokenResp
	err = json.Unmarshal(all, &at)
	if err != nil {
		emlogrus.Error("刷新accessToken错误：", err)
		return
	}
	if at.Errcode != 0 {
		emlogrus.Error("刷新accessToken错误：", at.Errcode, ",", at.Errmsg)
		return
	}
	AccessToken = at.AccessToken
	emlogrus.Info("token expiresIn", at.ExpiresIn)
	time.AfterFunc(time.Duration(at.ExpiresIn-4*60)*time.Second, func() {
		refreshAccessToken()
	})
}

func onceRefreshAccessToken() {
	get, err := http.Get(GetAccessTokenApi)
	if err != nil {
		emlogrus.Error("刷新accessToken错误：", err)
		return
	}
	defer func() {
		_ = get.Body.Close()
	}()
	all, err := io.ReadAll(get.Body)
	if err != nil {
		emlogrus.Error("刷新accessToken错误：", err)
		return
	}
	var at AccessTokenResp
	err = json.Unmarshal(all, &at)
	if err != nil {
		emlogrus.Error("刷新accessToken错误：", err)
		return
	}
	if at.Errcode != 0 {
		emlogrus.Error("刷新accessToken错误：", at.Errcode, ",", at.Errmsg)
		return
	}
	AccessToken = at.AccessToken
}

type ApiReturnResp struct {
	Errcode int
	Errmsg  string
}

// 生成小程序码
func GenWxQrcode(scene, page string, ossKey string) error {
	m := make(map[string]string)
	m["scene"] = scene
	if page != "" {
		m["page"] = page
	}
	marshal, err := json.Marshal(&m)
	if err != nil {
		return err
	}
	buffer := bytes.NewBuffer(marshal)
	post, err := http.Post(GetUnlimitedApi+AccessToken, "application/json", buffer)
	if err != nil {
		return err
	}
	defer func() {
		_ = post.Body.Close()
	}()
	ct := post.Header.Get("content-type")
	if ct == "image/jpeg" {
		return oss.Put(ossKey, post.Body)
	}
	all, err := io.ReadAll(post.Body)
	var qr ApiReturnResp
	err = json.Unmarshal(all, &qr)
	if err != nil {
		return err
	}
	if qr.Errcode == AccessTokenInvalidErrCode {
		onceRefreshAccessToken()
	}
	return errors.New(qr.Errmsg)
}

// 发送订阅消息
func SendSubscribeMessage(openid, templateId string, data map[string]interface{}) error {
	m := make(map[string]interface{})
	m["touser"] = openid
	m["template_id"] = templateId
	m["data"] = data
	marshal, err := json.Marshal(&m)
	if err != nil {
		return err
	}
	buffer := bytes.NewBuffer(marshal)
	post, err := http.Post(MessageSubscribeSendApi+AccessToken, "application/json", buffer)
	if err != nil {
		return err
	}
	defer func() {
		_ = post.Body.Close()
	}()
	all, err := io.ReadAll(post.Body)
	var ar ApiReturnResp
	err = json.Unmarshal(all, &ar)
	if err != nil {
		return err
	}
	if ar.Errcode != 0 {
		return errors.New(ar.Errmsg)
	}
	return nil
}

// 生成外部UrlLink
func GenUrlLink() (string, error) {
	m := make(map[string]interface{})
	m["is_expire"] = true
	m["expire_type"] = 1
	// 7天内有效
	m["expire_interval"] = 7
	marshal, err := json.Marshal(&m)
	if err != nil {
		return "", err
	}
	buffer := bytes.NewBuffer(marshal)
	post, err := http.Post(UrlLinkApi+AccessToken, "application/json", buffer)
	if err != nil {
		return "", err
	}
	defer func() {
		_ = post.Body.Close()
	}()
	all, err := io.ReadAll(post.Body)
	var resp = struct {
		Errcode int
		Errmsg  string
		UrlLink string `json:"url_link"`
	}{}
	err = json.Unmarshal(all, &resp)
	if err != nil {
		return "", err
	}
	if resp.Errcode != 0 {
		return "", errors.New(resp.Errmsg)
	}
	return resp.UrlLink, nil
}
